/*
 Ajax中的“串行”&“并行”：多个ajax请求的管理模式
   + 串行：一般是多个请求之间存在依赖(例如:当前请求需要用到上一个请求获取的结果)，此时我们只能等待上一个请求成功，才能发送下一个请求...
   + 并行：多个请求之间没有依赖，可以同时发送，谁先请求回来，就先处理谁

 Promise：ES6新增的内置类，用来管理异步编程代码，避免回调地狱 ->“承诺者设计模式”
   + 不兼容IE（如果需要兼容IE，则基于@babel/polyfill处理「原理：重写了Promise」）
   + 基于“new”使用，例如：new Promise(...)

 基础语法：let 实例对象 = new Promise([executor]);
   + [executor]必须传，而且是传递一个函数，否则会报错
   + [executor]函数执行是“同步”的：在“new Promise”的时候，就会把函数“立即”执行
      + promise内部在执行此函数的时候，还会给函数传递实参值，我们基于resolve/reject形参变量获取这个值；resolve/reject存储的值是个小函数，后期把相应的函数执行，会修改实例的状态和值！！

 实例对象的研究
   [私有属性/方法]
      [[PromiseState]]: "pending"  实例的状态“pending”准备、“fulfilled”成功、“rejected”失败
      [[PromiseResult]]: undefined 实例的值
   [Promise.prototype公共属性方法]
      then
      catch
      finally
      Symbol.toStringTa: "Promise"
 */

/*
 创建实例的第三种方案：let p2 = p1.then([onfulfilled],[onrejected]) 
   每一次执行THEN方法，p1的状态会决定onfulfilled/onrejected谁执行，也会创建一个全新的promise实例p2！
   如何修改p2的状态和值？
     总则：不论是onfulfilled还是onrejected执行，函数执行的一些细节决定了新实例p2的状态和值
     + 函数执行是否报错，如果报错了，则p2是rejected，值是报错原因；
     + 如果没报错，再看函数执行的返回值
       + 如果返回值是另外一个promise实例（名字：OTHER），则“OTHER”的状态和值决定了p2的状态和值
       + 如果不是promise实例，则p2状态是成功，值是函数的返回值
       + 如果没有写返回值，则p2状态是成功，值是undefined

 这种机制可以保证一直.then下去，我们把其称之为“THEN链机制”！

 THEN链具备“穿透/顺延”性！
   onfulfilled或者onrejected可以不传递
     p.then(onfulfilled)
     p.then(null,onrejected)
     p.then()
  如果不传递，则顺延至下一个同等状态要执行的方法上！
     原理：我们不设置onfulfilled/onrejected，在promise内部会帮我们设置默认值
     默认加的函数，可以保证状态和值的顺延！！

 p.catch([onrejected]) === p.then(null,[onrejected])
 */

/* 
// 真实项目中，一般then中只传递onfulfilled，在最末尾设置.catch(也就是onrejected)
// 好处：then中只处理状态为成功要做的事情，不论在何时遇到失败的实例，最后都会顺延至最后一个catch中进行处理
Promise.resolve(100)
    .then(value => {
        console.log('成功：', value);
        return value / a;
    })
    .then(value => {
        console.log('成功：', value);
        return value / 10;
    })
    .catch(reason => {
        console.log('失败：', reason);
    })
    .finally(() => {
        // 不论实例状态成功还是失败，最后都要执行这里{一般不用}
    }); 
*/

/*
Promise.resolve(100)
    .then(/!* value => {
        return value;
    } *!/)
    .then(value => {
        console.log('成功：', value); //成功：100
    }, reason => {
        console.log('失败：', reason);
    });
*/

/*
Promise.reject(100)
    .then(null, /!* reason => {
        throw reason;
    } *!/)
    .then(value => {
        console.log('成功：', value);
    }, reason => {
        console.log('失败：', reason); //失败：100
    });
*/


/* Promise.resolve(100)
    .then(value => {
        console.log('成功：', value);
        return Promise.reject(value / 10);
    }, reason => {
        console.log('失败：', reason);
        return Promise.resolve(reason * 10);
    })
    .then(value => {
        console.log('成功：', value);
        return value / 10;
    }, reason => {
        console.log('失败：', reason);
        return reason * 10;
    })
    .then(value => {
        console.log('成功：', value);
        return value / a;
    }, reason => {
        console.log('失败：', reason);
        return reason * b;
    })
    .then(value => {
        console.log('成功：', value);
    }, reason => {
        console.log('失败：', reason);
    })
    .then(value => {
        console.log('成功：', value);
    }, reason => {
        console.log('失败：', reason);
    }); */

/* let p1 = Promise.reject(10);
let p2 = p1.then(value => {
    return value / 10;
}, reason => {
    // return reason * 10;
    return Promise.resolve(reason);
});
console.log(p2); // fulfilled 10 */

/* let p1 = Promise.resolve(100);
let p2 = p1.then(value => {
    return a * value;
}, reason => {
    return 10;
});
console.log(p2); // rejected 报错原因 */

/* let p1 = Promise.reject(100);
let p2 = p1.then(value => {
    console.log('成功：', value);
    return 20;
}, reason => {
    console.log('失败', reason);
    return 10;
});
console.log(p2); // fulfilled 10 */

//=======================
/*
 创建实例的第二种方案：Promise.resolve/reject
   Promise.resolve(100) 创建一个状态是成功fulfilled，值是100的实例
   Promise.reject(0) 创建一个状态是失败rejected，值是0的实例
 */
/*
Promise.resolve(100)
    .then(value => {
        console.log('成功：', value);
    }, reason => {
        console.log('失败', reason);
    });
*/

//=======================
/*
 Promise是基于承诺者设计模式，来管理异步编程代码的！
 */
/* let p = new Promise((resolve, reject) => {
    // executor函数中用来管理异步代码，当异步操作结束，基于resolve/reject修改实例状态，从而决定then中传递的函数该执行哪一个！！
    setTimeout(() => {
        resolve(100);
    }, 2000);
});
p.then(value => {
    console.log('成功：', value);
}, reason => {
    console.log('失败', reason);
}); */

/*
// 基于“回调函数方式”管理异步操作(都是异步结束后干点啥事) ，很容易导致回调地狱
const fn = (callback) => {
    setTimeout(() => {
        callback(100);
    }, 2000);
}
fn(value => {
    console.log('成功：', value);
});
*/

//=======================
/*
 promise实例.then([onfulfilled],[onrejected])
   根据实例的状态,决定执行哪一个函数！
   + 如果实例状态是pending，则两个函数暂时都不执行
   + 如果状态是成功fulfilled，则执行[onfulfilled]
   + 如果状态是失败rejected，则执行[onrejected]
   + 把实例的值传递给相关的函数，所以value/reason获取的就是当前实例的值
 “同一个实例”可以多次调用then方法(虽然这种操作很少见)，这样在知道实例的状态后，会把每一次then中传递的指定函数，都触发执行！！
 */
/* let p = new Promise((resolve, reject) => {
    reject(0);
});
p.then(
    // onfulfilled
    (value) => {
        console.log('成功：', value);
    },
    // onrejected
    (reason) => {
        console.log('失败', reason);
    }
);
p.then(value => {
    console.log('成功：', value);
}, reason => {
    console.log('失败', reason);
}); */

//=======================
/*
 创建promise实例的第一种方案：new Promise((resolve, reject)=>{ ... })
   实例的状态和值是如何被修改的？
   + 基于resolve/reject执行，可以修改实例的状态和值
   + executor函数执行报错
 */
/* let p = new Promise((resolve, reject) => {
    // 一但状态值从pending改为fulfilled或者rejected，则不能再修改了
    // resolve(100);  //把实例状态修改为fulfilled，值是100
    // reject(0); //把实例状态修改为rejected，值是0

    // 如果executor函数执行报错，则实例状态为rejected，值是报错原因
    // console.log(a);
});
console.log(p);

// let p = new Promise(); //Uncaught TypeError: Promise resolver undefined is not a function
// let p = new Promise({}); //Uncaught TypeError: Promise resolver #<Object> is not a function */

//=======================
const queryAPI1 = () => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(1);
        }, 2000);
    });
};
const queryAPI2 = () => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(2);
        }, 1000);
    });
};
const queryAPI3 = () => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(3);
        }, 3000);
    });
};

/* // async&await:Promise语法糖，主要简化Promise代码！！
(async () => {
    let value = await queryAPI1();
    console.log('请求一成功：', value);

    value = await queryAPI2();
    console.log('请求二成功：', value);

    value = await queryAPI3();
    console.log('请求三成功：', value);
})(); */


/* // 串行
queryAPI1()
    .then(value => {
        console.log('请求一成功：', value);
        return queryAPI2();
    })
    .then((value) => {
        console.log('请求二成功：', value);
        return queryAPI3();
    })
    .then((value) => {
        console.log('请求三成功：', value);
    })
    .catch(reason => {
        // 但凡有一个请求是失败的，都执行这里
    }); */

//=======================
/* // 回调函数方式：基于定时器模拟出异步ajax请求效果
const queryAPI1 = (callback) => {
    setTimeout(() => {
        let result = 1; //假设这是从服务器获取的结果
        callback(result);
    }, 2000);
};
const queryAPI2 = (callback) => {
    setTimeout(() => {
        let result = 2;
        callback(result);
    }, 1000);
};
const queryAPI3 = (callback) => {
    setTimeout(() => {
        let result = 3;
        callback(result);
    }, 3000);
}; */

/* // 串行：总共需要6秒 
// 如果基于“回调函数方式”封装数据请求，则很容易导致回调地狱「回调嵌套回调」
queryAPI1((result) => {
    console.log('请求一成功：', result);

    queryAPI2((result) => {
        console.log('请求二成功：', result);

        queryAPI3((result) => {
            console.log('请求三成功：', result);
        });
    });
}); */

/* // 并行:总共需要3秒
queryAPI1((result) => {
    console.log('请求一成功：', result);
});
queryAPI2((result) => {
    console.log('请求二成功：', result);
});
queryAPI3((result) => {
    console.log('请求三成功：', result);
}); */